﻿
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Skybot.BaseSocket
{
    /// <summary>
    /// TCP 数据处理对像
    /// </summary>
    public class TCPAsyncServer
    {
        /// <summary>
        /// 当前Socket 用作服务端的监听 对像
        /// </summary>
        private System.Net.Sockets.Socket listenSocket = null;

        /// <summary>
        /// 用于接受数据的最大缓冲区250KB
        /// </summary>
        private int MaxBuffSize = 1024 * 250;
        /// <summary>
        /// 限制访问同一资源的线程数
        /// </summary>
        private System.Threading.Semaphore SemaphoreAccepted;

        /// <summary>
        /// 当联接完成后的事件
        /// </summary>
        public event EventHandler<SocketAsyncEventArgs> OnAccepted;

        /// <summary>
        /// 用于监听数据的SocketEventargs
        /// </summary>
        private SocketAsyncEventArgs listenSocketAsyncEventArgs = null;

        /// <summary>
        /// 联接的客户端对像
        /// </summary>
        private Dictionary<string, SocketAsyncEventArgs> clients = new Dictionary<string, SocketAsyncEventArgs>();
        /// <summary>
        /// 联接的客户端对像集合,以远程终结点为主键,注意此数据不要对集合进行修改,否则将出现联接状态维护问题
        /// </summary>
        public Dictionary<string, SocketAsyncEventArgs> Clients
        {
            get { return clients; }
        }
        /// <summary>
        /// 客户端联接断开事件
        /// </summary>
        public event EventHandler<TCPAsyncServerCloseConnEventArgs> OnClientClosed;

        /// <summary>
        /// 服务处理
        /// </summary>
        public Action<SocketAsyncEventArgs, EndPoint> Handler { get; set; }

        private int currentConnectionCount = 0;

        /// <summary>
        /// 当前已经联接上的客户端总数
        /// </summary>
        public int CurrentConnectionCount
        {
            get { return currentConnectionCount; }
        }

        /// <summary>
        /// 锁对像
        /// </summary>
        protected object LockObject = new object();


        public int MaxClientCount { get; set; }
        /// <summary>
        /// 创建一个新的TCP服务端
        /// </summary>
        /// <param name="maxTerminalCount">最大客户接入数</param>
        public TCPAsyncServer(int maxClientCount = 10000)
        {
            MaxClientCount = maxClientCount;
        }
        /// <summary>
        /// 开始监听对像
        /// </summary>
        /// <param name="ipAddress">IP地址,不填表示默认</param>
        /// <param name="port">端口</param>
        public void Listen(int port, IPAddress ipAddress = null)
        {
            if (SemaphoreAccepted == null)
            {
                SemaphoreAccepted = new System.Threading.Semaphore(MaxClientCount, MaxClientCount);
            }

            ipAddress = ipAddress == null ? IPAddress.Any : ipAddress;
            //创建一个TCP对像
            listenSocket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            listenSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

            //邦定端口
            listenSocket.Bind(new IPEndPoint(ipAddress, port));
            listenSocket.Listen(20);

            //放到线程池中运行
        System.Threading.Tasks.Task.Factory.StartNew(() =>
            {
                DoAcceptAsync(null);
                //LogProvider.Create().Write(EventType.Information, string.Format("{0},启动[ sock.tcp://{1} ]监听。", 100, new IPEndPoint(ipAddress, port)));
            });

        }

        /// <summary>
        /// 停止TCP服务
        /// </summary>
        /// <returns></returns>
        public bool Stop()
        {
            //循环
            foreach (var item in clients.ToArray())
            {
                try
                {
                    Socket cursocket = (item.Value.UserToken as Socket);
                    //关闭联接
                    cursocket.Close();
                    //cursocket.Dispose();
                    System.Threading.Interlocked.Decrement(ref currentConnectionCount);
                }
                catch
                {

                }
            }

            listenSocket.Close();
            //listenSocket.Dispose();

            return true;
        }

        /// <summary>
        /// 手动关闭客户端联接
        /// </summary>
        /// <param name="ep">远程客户端联接终结点</param>
        public bool CloseClient(IPEndPoint ep)
        {
            SocketAsyncEventArgs args;
            string epstr = ep.ToString();
            Clients.TryGetValue(epstr, out args);
            if (args != null)
            {
                CloseClientConnection(args, epstr);
                return true;
            }
            //没有找到对应的客户端联接对像
            return false;
        }

        /// <summary>
        /// 开始接受联接
        /// </summary>
        protected void DoAcceptAsync(SocketAsyncEventArgs args)
        {
            if (args == null)
            {
                //设置监听对像
                args = listenSocketAsyncEventArgs = new SocketAsyncEventArgs();
                //注意接收数据缓冲区 如果设置了以后,将不能在用户联接上来时,检查到有新的联接接入
                //byte[] buff = new byte[MaxBuffSize];
                // listenSocketAsyncEventArgs.SetBuffer(buff, 0, buff.Length);
                listenSocketAsyncEventArgs.Completed += listenSocketAsyncEventArgs_Completed;
            }
            else
            {
                args.AcceptSocket = null;
            }
            try
            {
                //同步完成按照msdn解释不会调用Comleted事件
                if (!listenSocket.AcceptAsync(listenSocketAsyncEventArgs))
                {
                    listenSocketAsyncEventArgs_Completed(listenSocket, listenSocketAsyncEventArgs);
                }
            }
            catch (SocketException)
            {
                System.Diagnostics.Trace.WriteLine("TCPAsyncServer监听服务已经停止....");
            }
            catch (ObjectDisposedException)
            {
                System.Diagnostics.Trace.WriteLine("TCPAsyncServer无法访问已经释放对象的监听服务....");
            }

        }

        /// <summary>
        /// 监听工作完成 有新的客户端接入
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void listenSocketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs e)
        {
            if (e.AcceptSocket != null)
            {

                //从池不取出一个  receiveArgs 对像
                SocketAsyncEventArgs receiveArgs = RentReveiveSocketArgs();
                //是不是超过最大联接数
                if (currentConnectionCount > MaxClientCount)
                {
                    CloseClientConnection(receiveArgs);
                }
                else
                {
                    try
                    {
                        //当前对像用户Socket
                        receiveArgs.UserToken = e.AcceptSocket;
                        if (!e.AcceptSocket.ReceiveAsync(receiveArgs))
                        {
                            IO_ReceiveComleted(e.AcceptSocket, receiveArgs);
                        }

                        //添加到对像集合
                        lock (LockObject)
                        {
                            try
                            {
                                if (e.AcceptSocket.RemoteEndPoint != null)
                                {
                                    clients.Add(e.AcceptSocket.RemoteEndPoint.ToString(), e);
                                    //当前联接加1
                                    System.Threading.Interlocked.Increment(ref currentConnectionCount);

                                }
                            }
                            catch (System.ObjectDisposedException)
                            {
                                //socket资源释放
                            }
                        }
                        //添加事件通知
                        if (OnAccepted != null)
                        {
                            OnAccepted(this, e);
                        }

                    }
                    catch (SocketException)
                    {
                        //关闭联接还池
                        CloseClientConnection(receiveArgs);
                    }
                }

            }
            //开始接受下个联接
            DoAcceptAsync(e);
        }



        #region 数据接收
        /// <summary>
        /// 开始接受数据
        /// </summary>
        /// <param name="e"></param>
        private void StartReceive(SocketAsyncEventArgs e)
        {
            //没有传入Socket联接
            if (e.UserToken == null)
            {
                return;
            }
            Socket cursocket = e.UserToken as Socket;
            if (cursocket == null)
            {
                return;
            }
            //联接正常
            if (cursocket.Connected)
            {
                try
                {
                    if (!cursocket.ReceiveAsync(e))
                    {
                        IO_ReceiveComleted(cursocket, e);
                    }
                }
                catch (System.InvalidOperationException err)
                {

                    Console.WriteLine("TCPAsyncServer.StartReceive　方法执行失败。错误：" + err);
                    //无效操作异常
                    CloseClientConnection(e);
                }

            }
            else
            {
                CloseClientConnection(e);
            }
        }

        /// <summary>
        /// 客户端接受数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void IO_ReceiveComleted(object sender, SocketAsyncEventArgs e)
        {
            try
            {
                if (e.SocketError == SocketError.Success && e.BytesTransferred > 0)
                {
                    //转到接收数据
                    ProcessReceive(e);
                }
                else
                {
                    if (e.BytesTransferred == 0)
                    {
                        CloseClientConnection(e);
                    }
                }
            }
            catch (SocketException err)
            {
                throw err;
            }
            StartReceive(e);
        }

        /// <summary>
        /// 关闭指定的联接
        /// </summary>
        /// <param name="e">socket对像</param>
        /// <param name="ep">关闭对像</param>
        private void CloseClientConnection(SocketAsyncEventArgs e, string ep = null)
        {
            //没有传入Socket联接
            if (e.UserToken == null && string.IsNullOrEmpty(ep))
            {
                return;
            }
            Socket cursocket = e.UserToken as Socket;

            if (cursocket == null && string.IsNullOrEmpty(ep))
            {
                return;
            }
            //客户端联接终结点
            IPEndPoint curRemondpoint = null;
            try
            {
                curRemondpoint = (IPEndPoint)cursocket.RemoteEndPoint;
            }
            catch (NullReferenceException)
            {
            }
            catch (SocketException)
            {
            }
            catch (ObjectDisposedException)
            {
            }
            //远程客户端终结点,创建这个对像主要是因为,下面需要用于客户端关闭通知事件
            EndPoint remoteEndPoint = null;
            //创建客户端联接终节点
            if (curRemondpoint != null)
            {
                remoteEndPoint = new IPEndPoint(curRemondpoint.Address, curRemondpoint.Port);
            }
            else
            {
                if (!string.IsNullOrEmpty(ep))
                {
                    remoteEndPoint = new IPEndPoint(IPAddress.Parse(ep.Split(':')[0]), int.Parse(ep.Split(':')[1]));
                }
            }

            //删除当前不需要的联接
            lock (LockObject)
            {
                if (remoteEndPoint != null)
                {
                    if (clients.ContainsKey(remoteEndPoint.ToString()))
                    {
                        clients.Remove(remoteEndPoint.ToString());
                        //当前联接数
                        System.Threading.Interlocked.Decrement(ref currentConnectionCount);

                    }
                }
            }
            if (cursocket == null)
            {
                return;
            }
            try
            {
                cursocket.Shutdown(SocketShutdown.Both);
            }
            catch (SocketException)
            {
            }
            catch (ObjectDisposedException)
            {
            }
            try
            {
                //关闭联接
                cursocket.Close();
            }
            catch (SocketException)
            {
            }
            catch (ObjectDisposedException)
            {
            }

            //还池
            GivebackReceiveSocketArgs(e);

            //调用客户端关闭事件
            if (OnClientClosed != null)
            {
                OnClientClosed(this, new TCPAsyncServerCloseConnEventArgs(remoteEndPoint));
            }
        }

        /// <summary>
        /// 接受数据
        /// </summary>
        /// <param name="e">当前用于接收数据的SocketEventArgs</param>
        protected void ProcessReceive(SocketAsyncEventArgs e)
        {
            //var data = new byte[e.BytesTransferred];
            //Buffer.BlockCopy(e.Buffer, e.Offset, data, 0, e.BytesTransferred);

            if (Handler != null)
            {
                Socket cursocket = e.UserToken as Socket;
                if (cursocket == null)
                {
                    return;
                }
                Handler(e, cursocket.RemoteEndPoint);
            }
        }
        #endregion


        #region 客户端联接池
        /// <summary>
        /// 接收SAEA池集合
        /// </summary>
        private readonly ConcurrentStack<SocketAsyncEventArgs> ReceiveSocketArgsPool = new ConcurrentStack<SocketAsyncEventArgs>();

        /// <summary>
        /// 初始化接收socketargs对象池
        /// </summary>
        private void InitReceiveSocketArgsPool()
        {
            for (int ndx = 0; ndx < MaxClientCount * 2; ndx++)
            {
                ReceiveSocketArgsPool.Push(CreateReceiveSocketArgs());
            }
        }

        /// <summary>
        /// 创建一个接收SAEA对象,设置最大接收字节数
        /// </summary>
        /// <returns></returns>
        private SocketAsyncEventArgs CreateReceiveSocketArgs()
        {
            SocketAsyncEventArgs e = new SocketAsyncEventArgs();
            //接收数据缓冲区大小150KB
            byte[] buff = new byte[MaxBuffSize];
            //设置最大接收字节数
            e.SetBuffer(buff, 0, buff.Length);
            e.Completed += IO_ReceiveComleted;

            return e;
        }



        /// <summary>
        /// 租赁一个接收SAEA对象
        /// </summary>
        /// <returns></returns>
        private SocketAsyncEventArgs RentReveiveSocketArgs()
        {
            SocketAsyncEventArgs e;

            return ReceiveSocketArgsPool.TryPop(out e) ? e : CreateReceiveSocketArgs();
        }

        /// <summary>
        /// 归还一个接收SAEA参数
        /// </summary>
        /// <param name="e">还池</param>
        private void GivebackReceiveSocketArgs(SocketAsyncEventArgs e)
        {
            e.UserToken = null;
            if (e != null) ReceiveSocketArgsPool.Push(e);
        }

        #endregion

    }
}
